<?php 

	/*
		script: smuoi.php
		The invocation script.
		(c) Clancy Hood 2010.
		
	----------------
			
	func: smuoi_getenv
		The remaining function for reading environment variables, including constants which can be used to set global defaults.	
		Unset / undefined env vars are returned as null.

		params: 
			N - (string) env var name
	*/	

	function smuoi_getenv($N){
		return (($e = getenv($N)) === false) ? (defined($N) ? smuoi_readenv(constant($N)) : null) : smuoi_readenv($e);
	}
	
	/*
	func: utime
		Returns *string* UNIX time to 6 decimal places (microseconds).
	*/

	function utime(){
		$t = explode(' ', microtime());
		return sprintf('%01.6f', $t[1] + $t[0]);
	}
	
	/*
	func: smuoi_explode
		Explodes, but removes any empty members.

		params:
			d - (string) delimiter
			s - (string) what to explode
	*/	

	function smuoi_explode($d, $s){
		return array_filter(explode($d, $s));
	}
	
	/*
	func: smuoi_unexplode
		Cleans path strings by exploding and imploding again.
		Accepts arguments in *opposite* order to explode functions such as <smuoi_explode>.
		Turns input such as /path//to///file/ into path/to/file.

		params:
			s - (string) what to clean
			d - (string) delimiter
	*/

	function smuoi_unexplode($s, $d='/'){
		return implode($d, smuoi_explode($d, $s));
	}
	
	/*
	func: smuoi_clean_request_uri
	 	<smuoi_unexplode>s a request uri, strips query string if present.

		params: 
			u - (string) uri to clean
	*/

	function smuoi_clean_request_uri($u){
		if($q = strpos(" $u", '?')) $u = substr($u, 0, $q-1);
		return urldecode(smuoi_unexplode($u));
	}
	
	/*
	func: array_pick
		Picks value from an array by key, if present.

	 	params:
			k - (string) key
			a - (array) array		
	*/

	function array_pick($k, array $a){
		return isset($a[$k]) ? $a[$k] : null;	
	}

	/*
	func: globget
		Fetches global variable set with <globset>.

		params: 
			k - (string) key
	*/	

	function globget($k){
		return array_pick("smuoi\\$k", $GLOBALS);
	}
	
	/*
	func: globset
		Sets global variables (with prefix smuoi\).

		params:
			k - (string) key
			v - (mixed) var
	*/
	
	function globset($k, $v){
		$GLOBALS["smuoi\\$k"] = $v;
	}
	
	/*
	func: smuoi_register_shutdown_function
	 	It's often useful to call shutdown functions in reverse order of registration. 
	 	For this we use own array and krsort it on shutdown.

		params:
			f - valid callback
	*/

	function smuoi_register_shutdown_function($f){
		$GLOBALS['SMUOI_SHUTDOWN_FUNCS'][] = $f;
	}
	
	/*
	func: smuoi_jump_shutdown_queue
	 	Sometimes we need to make absoutely sure a function is called first.
		This system predates the simpler method of registering and firing events.

		params:
			f - valid callback
	*/ 

	function smuoi_jump_shutdown_queue($f){
		$GLOBALS['SMUOI_SHUTDOWN_FUNCS'][utime()] = $f;	
	}
	
	/*
	func: smuoi_call_shutdown_funcs
		Called by PHP on exit.
	*/

	function smuoi_call_shutdown_funcs(){
		define('SMUOI_IS_EXITING', 1);
		if(is_array($a = array_pick('SMUOI_SHUTDOWN_FUNCS', $GLOBALS))){ 
			krsort($a);
			foreach($a as $f) call_user_func($f);
		}
		if(getenv('SMUOI_SHOW_STATS')) smuoi_print_stats();
	}
	
	/*
	func: smuoi_is_exiting
		Returns bool SMUOI/PHP is exiting.
	*/

	function smuoi_is_exiting(){
		return defined('SMUOI_IS_EXITING');
	}
	
	/*
	func: smuoi_print_stats
		Prints out memory usage (rather ambiguous), time in seconds the operation took, number of database queries performed and included file count.
	*/

	function smuoi_print_stats(){
		echo "<!-- M:".memory_get_usage().' T:'.sprintf('%01.6f', (utime() - SMUOI_TIME_IN)).' I:'.count(get_included_files())." !-->";
	}
	
	/*
	func: __autoload
	 	PHP's magic class autoloader.  Looks in <SMERNEL_PATH> for $class.php.

		params:
			class - classname to load
	*/

	function __autoload($class){
		//if(substr($class, 0, 1) != '_') $class = str_replace('_', '/', $class);
	
		require_once SMERNEL_PATH."/$class.php";
	}
	
	/*
	func: sm_startup_error		
		Quick-and-dirty error handler for errors on startup.
	*/
	
	function sm_startup_error($n, $str, $file, $line){
		$m = "$str in <b>$file</b> on line <b>$line</b>";
		if($n != E_STRICT) error_log($m);
		switch($n){
			case E_ERROR:
			case E_USER_ERROR:
				die($m);
		}
	}
	
	/*
	constants: Identfiers
		SMUOI_TIME_IN - Our earliest opportunity to hit the stopwatch.
		INSTANCE - Our unique instance id (typically a 40-char alphanumeric string with two periods).
		ARF - Apache Requested Filename. $_SERVER['REQUEST_URI'] stripped of query string.
		XTN - Extension of file that called this script.
	*/
	define('SMUOI_TIME_IN', utime());
	define('INSTANCE', uniqid(SMUOI_TIME_IN, true)); 
	define('ARF', smuoi_clean_request_uri($_SERVER['REQUEST_URI']));
	define('XTN', strtolower(substr(strrchr($_SERVER["SCRIPT_FILENAME"], '.'), 1)));
		
	/*
	constants: Host paths
		SMUOI_DIR - Directory that smuoi.php resides in
		PKG_DIR - Shorthand for <SMUOI_DIR>/Applications
		SMR_DIR - Shorthand for <SMUOI_DIR>/Smernel
	*/
	define('SMUOI_DIR', realpath(dirname(__FILE__)));
	define('PKG_DIR',  SMUOI_DIR.'/Applications');
	define('SMR_DIR',  SMUOI_DIR.'/Smernel');
		
	// Introduce a buffer - no performance impact until it is used.
	// If smuoi is not intended to run, the buffer will be deleted before handover
	if(!ob_get_level()) ob_start();

	// parse the global config file
	require SMUOI_DIR.'/smrc.php';
	
	// check we can write where we need to
	if(!is_writeable(SMUOI_PREFIX)) trigger_error(SMUOI_PREFIX.' must be writeable.', E_USER_ERROR);
	
	/*
	constants: More host paths
		ETC_DIR - Shorthand for <SMUOI_PREFIX>/etc
		LOG_DIR - Shorthand for <SMUOI_PREFIX>/cruft/log
		LOCAL_DIR - Shorthand for <SMUOI_PREFIX>/cruft/local
		TMP_DIR - Shorthand for <SMUOI_PREFIX>/cruft/tmp
	*/
	define('ETC_DIR',  SMUOI_PREFIX.'/etc');
	
	// define the remaining 3 with a loop
	foreach(array('log', 'local', 'tmp') as $c){
		// First we check if everything is in order and continue if so
		$C = strtoupper($c);
		$cdir = SMUOI_DIR."/cruft/$c";
		if(($p = realpath($cdir)) && is_dir($p) && is_writeable($p)){
			// orl korrect
			define($C.'_DIR', $cdir);
			continue;
		}
		// Something is uninitalised, or otherwise wrong.
		// Check constant, whether directory exists / try to make it, check is writeable.
		$T = constant('SMUOI_CRUFT_'.$C.'_TARGET');
		if((!$T) || (!is_dir($T) && !mkdir($T, 0755)) || !is_writeable($T)){
			if($T) trigger_error("Could not mkdir or write to $T, defaulting to $cdir", E_USER_WARNING);
			// we assume SMUOI_PREFIX/cruft is writeable, since SMUOI_PREFIX is.
			if(is_file($cdir)) unlink($cdir);
			mkdir($cdir, 0755);
			define($C.'_DIR', $cdir);
			continue;
		}
		// again, cruft should be writeable
		if(is_file($cdir)) unlink($cdir);
		exec("ln -s $T $cdir");
		define($C.'_DIR', $cdir);
	}
	
	// until the domain is up, generic error log location
	ini_set('error_log', LOG_DIR.'/smuoi-'.date('Y-m-d@H').'h.log'); 
	
	// quick-and-dirty error handler for startup
	set_error_handler('sm_startup_error');
	
	// register our shutdown caller
	register_shutdown_function('smuoi_call_shutdown_funcs');
	
	/*
	constants: Context
		PUBLIC_DOMAIN - Bool whether we are in a public or private (desktop) context
		CONTEXT - 3-char (dom|dsk) representation of the above
	*/
	define('PUBLIC_DOMAIN', (getenv('SMUOI') !== 'smrde'));	
	define('CONTEXT', (PUBLIC_DOMAIN ? 'dom' : 'dsk'));
	
	/*
	constants: deprecated
		SHR - Path below desktop domain to public files (no authentication)
		SHR_ROOT - Path on host to the above
	*/
	define('SHR', '/@public');
	define('SHR_ROOT', DSK_ROOT.SHR);

	// Now we can orientate ourselves
	$dom = $_SERVER['SERVER_NAME'];
	$cnf = ETC_DIR."/dom/$dom";
	if(!$DOM_INI = @parse_ini_file($cnf, 1)) trigger_error("$dom is not registered here ($cnf).", E_USER_ERROR);

	/*
	constants: Orientation
	 	SERVER_NAME - $_SERVER['SERVER_NAME']
	 	DOM_ID - ID of domain
	 	SMERNEL - Smernel version to use
	 	SMERNEL_PATH - Shorthand for SMR_DIR/<SMERNEL>
	*/
	define('SERVER_NAME', $_SERVER['SERVER_NAME']);		
	define('DOM_ID', $DOM_INI['DOM_ID']);
	define('SMERNEL', $DOM_INI['SMERNEL']);
	define('SMERNEL_PATH', SMR_DIR.'/'.SMERNEL);

	// __autoload now comes into play
	$GLOBALS['SMERNEL'] = new Smernel;

	// This belongs in installation check
	if (get_magic_quotes_gpc()) trigger_error('get_magic_quotes is DEPRECATED - TURN IT OFF', E_USER_ERROR);

### Let's go!
	Domain()->run();
	
### And that's that.

